package hr.sting.beans;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * @author mstipanov
 * @since 09.07.2010. 23:34:02
 */
public class BeanInspector {
    private Map<String, PropertyInfo> propertyMap = new HashMap<String, PropertyInfo>();
    private Class<?> type;

    public BeanInspector(Class<?> type) {
        this.type = type;
    }

    public PropertyInfo[] getProperties() {
        for (Method method : type.getMethods()) {
            append(method);
        }

        for (Field field : type.getDeclaredFields()) {
            append(field);
        }

        Collection<PropertyInfo> infos = propertyMap.values();
        return infos.toArray(new PropertyInfo[infos.size()]);
    }

    private void append(Field field) {
        String propertyName = getPropertyName(field);
        if (null == propertyName) {
            return;
        }

        if (!propertyMap.containsKey(propertyName)) {
            propertyMap.put(propertyName, new PropertyInfo(propertyName));
        }
        PropertyInfo propertyInfo = propertyMap.get(propertyName);

        propertyInfo.setPropertyField(field);
    }

    private void append(Method method) {
        String propertyName = getPropertyName(method);
        if (null == propertyName) {
            return;
        }

        if (!propertyMap.containsKey(propertyName)) {
            propertyMap.put(propertyName, new PropertyInfo(propertyName));
        }
        PropertyInfo propertyInfo = propertyMap.get(propertyName);
        if (isSetter(method)) {
            propertyInfo.setSetterMethod(method);
            return;
        }

        propertyInfo.setGetterMethod(method);
    }

    private String getPropertyName(Field field) {
        return fixCase(field.getName());
    }

    private String getPropertyName(Method method) {
        String methodName = method.getName();
        boolean setter = false;

        if (!isGetter(method) && !(setter = isSetter(method))) {
            return null;
        }

        String name = fixCase(methodName.substring(3));

        if (setter) {
            return name;
        }

        if (methodName.startsWith("g")) {
            return name;
        }

        name = fixCase(methodName.substring(2));
        return name;

    }

    private String fixCase(String name) {
        if (name.length() == 1) {
            return name.toLowerCase();
        }

        String secondChar = name.substring(1, 2);
        if (secondChar.equals(secondChar.toUpperCase())) {
            return name;
        }

        return name.substring(0, 1).toLowerCase() + name.substring(1);
    }

    private boolean isGetter(Method method) {
        String methodName = method.getName();

        return ((methodName.startsWith("get") && methodName.length() > 3)
                || (methodName.startsWith("is") && methodName.length() > 2))

                && method.getParameterTypes().length == 0;
    }

    private boolean isSetter(Method method) {
        String methodName = method.getName();

        return methodName.startsWith("set") && methodName.length() > 3
                && method.getParameterTypes().length == 1;
    }

}
